feat(🐎): drive multiple animated props from a single shared value (select)#3899
Open
Grassper wants to merge 8 commits into
Open
feat(🐎): drive multiple animated props from a single shared value (select)#3899Grassper wants to merge 8 commits into
Grassper wants to merge 8 commits into
Conversation
…value
Previously each animated prop required its own shared value. This adds a
wrapper ({ __sv, __key }) so a single shared value whose `.value` is an
object can drive multiple props, each reading one key. The Reanimated
recorder registers the underlying shared value once (deduped on __sv), and
the native converter resolves `sharedValue.value[__key]` per prop on the UI
thread.
Author
|
Ran e2e locally on iOS — 399 pass. The one failure ( |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What problem this solves
Today every animated Skia prop must be backed by its own shared or derived value. When many props derive from one source — e.g. several elements driven by a single real-time data stream — this has a few costs:
useDerivedValueis its own Reanimated mapper running on the UI thread. N props → N subscriptions, even when they all come from the same source.selectaddresses all of these by letting a single shared value — whose value is an object — drive many props, one key each. The animated state is grouped in one place, Reanimated subscribes once regardless of how many props read from it, adding a field is just adding a key, and there's a single source of truth with no derived-value chains.What changed
select(sharedValue, key)binds a prop to a single key of a "grouped" shared value (one whose.valueis an object):Public API
select(sharedValue, key)— creates the binding (typed: a wrong key/value type is a compile error for concrete object types)SharedValueSelector<T>— added to theAnimatedProp<T>unionisSharedValueSelector— runtime guardImplementation
Convertor.h): resolvessharedValue.value[key]per prop on the UI thread;ReanimatedRecorderregisters the underlying shared value once (deduped) even when many props select from it.Recorder.processProps+materializeCommand): same behaviour on the JS path — collect/dedup the shared value, resolve the key each frame.Both paths skip transient non-applicable values (
null/undefined, and an un-ticked animation function) rather than crashing; genuine type mismatches still surface as errors.How to test
SharedValueComparison). The same 72-prop animation is wired two ways; the toggle flips between 1 Reanimated mapper (grouped) and 72 (per-prop).packages/skia, runyarn test src/sksg/__tests__/SharedValueSelector.spec.ts— covers the guard,select, webmaterializeCommandresolution, and recorder dedup.Notes / limitations
Reanimated only animates values assigned directly to
.value, not values nested inside an object, so per-keywithTiming/withSpring({ cx: withTiming(100) }) is not supported — assign plain values to the object, or build it in auseDerivedValue. Documented in the Animations page.Test-coverage boundary: the guard and the full web path are unit-tested. The native
ReanimatedRecorderandConvertor.haren't unit-testable in the jest suite (Skia.Recorder()is native-only; there's no C++ test harness) — they share the same dedup/guard logic and were verified manually via the example.When to use it:
selectis best when several props share one source that updates together (e.g. a single data tick). Because any key change re-applies every prop bound to that value, props that change independently — or very large objects — may be better served by separate shared values.Video example
groupedSharedVallue.mov